package org.com.Service.Impl;

import Entity.OrderVoucher;
import Entity.Voucher;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.com.Mapper.VoucherMapper;
import org.com.Service.IOrderVoucherService;
import org.com.Service.IVoucherService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 277
 * @since 2025-09-17
 */
@Slf4j
@Service
public class VoucherServiceImpl extends ServiceImpl<VoucherMapper, Voucher> implements IVoucherService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private IOrderVoucherService orderVoucherService;

    @Lazy
    @Autowired
    private IVoucherService voucherService;


    //使用lua脚本

    private static final DefaultRedisScript<Long> SECKLII_SCRIPT;

    //初始化
    static {
        SECKLII_SCRIPT = new DefaultRedisScript<>();
        SECKLII_SCRIPT.setLocation(new ClassPathResource("voucher.lua"));
        SECKLII_SCRIPT.setResultType(Long.class);
    }

    private static final String SECKILL_STOCK = "seckill:stock:";
    private static final String SECKILL_USER = "seckill:user:";
    private static final String SECKILL_ORDER = "seckill:order:";
    private static final String LOGIN_USER = "login:user:";

    @Transactional
    @Override
    public String seckillbyid(int voucherid) throws InterruptedException {

        //查询redis
        Boolean exsist = checkWithredis(voucherid);
        if (!exsist) {
            return "商品不存在";
        }

        //查询用户
        int userid = Integer.parseInt(stringRedisTemplate.opsForValue().get(LOGIN_USER));
        if (userid == 0) {
            log.info("用户不存在");
            return "用户不存在";
        }
        RLock rLock = redissonClient.getLock(String.valueOf(userid));
        boolean success = false;
        try {
            success = rLock.tryLock(1, 5, TimeUnit.MINUTES);
            if (!success | Thread.currentThread().isInterrupted()) {
                //获取锁失败
                return "用户不存在";
            }
            //获取锁成功 对用户+商品上锁
            RLock shopLock = redissonClient.getLock(LOGIN_USER + SECKILL_STOCK);
            try {
                success = shopLock.tryLock(1, 5, TimeUnit.MINUTES);
                if (!success | Thread.currentThread().isInterrupted()) {
                    //获取锁失败
                    return "商品不存在";
                }
//获取成功 执行lua脚本
                Long execute = stringRedisTemplate.execute(SECKLII_SCRIPT,
                        Collections.emptyList(),
                        String.valueOf(userid),
                        String.valueOf(voucherid));
                if (execute != 0) {
                    return execute == -1 ? "库存不足" : (execute == -2) ? "重复下单" : ("系统错误");
                }
                //有库存且未下单
                //异步下单创建命令

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                if (success) {
                    shopLock.unlock();
                }
            }

        } finally {
            if (success)
                rLock.unlock(); //释放锁
        }

        createOrder(userid, voucherid);

        //否则返回
        return "成功下单";
    }

    private Boolean checkWithredis(int voucherid) {
        //从redis中获取
        String s = stringRedisTemplate.opsForValue().get(SECKILL_STOCK + voucherid);
        if (s != null) {
            log.info("redis查到数据");
            return true;
        }
        log.info("redis中无此数据 即将进行数据库");
        //不存在 在数据库中获取

        Voucher voucher = this.getById(voucherid);
        if (voucher == null) {
            log.info("数据库无此数据");
            return false;
        }
        //从数据库写入
        try {
            stringRedisTemplate.opsForValue().set(SECKILL_STOCK + voucherid, String.valueOf(voucher.getVoucherStock()));
            stringRedisTemplate.expire(SECKILL_STOCK + voucherid, 50, TimeUnit.MINUTES);
            log.info("redis写入成功");
            return true;
        } catch (Exception e) {
            log.error("redis写入失败");
            return false;
        }
    }

    @Transactional
    protected void createOrder(int userid, int voucherid) {

        //OrderVoucher表 数据库兜底检查
        boolean exsist = orderVoucherService.lambdaQuery().eq(OrderVoucher::getUserId, userid)
                .eq(OrderVoucher::getVoucherId, voucherid)
                .count() > 0;
        //判断
        if (exsist) {
            log.error("已经购买过,请勿重复下单");
            return;
        }
        //减库存

        boolean update = lambdaUpdate().eq(Voucher::getId, voucherid)
                .gt(Voucher::getVoucherStock, 0)
                .setSql("voucher_stock=voucher_stock-1")
                .update();
        if (!update) {
            log.error("库存不足");
        }
        log.info("减库存成功");

        HashMap<String, Object> map = new HashMap<>();
        map.put("userid", userid);
        map.put("voucherid", voucherid);
        //发送消息 生成订单
        rabbitTemplate.convertAndSend("order.exchange", "voucher订单创建", map);


    }

    @Transactional
    @Override
    public Voucher saveWithRedis(Voucher voucher) {
        this.save(voucher);

        //从数据库查是否存在
        Voucher vouchers = voucherService.getById(voucher.getId());
        if (vouchers == null) {
            log.info("数据库无此数据");
            throw new RuntimeException("数据库无此数据");
        }
        //初始化库存
        stringRedisTemplate.opsForValue().set(SECKILL_STOCK + vouchers.getId(), String.valueOf(vouchers.getVoucherStock()));
        stringRedisTemplate.expire(SECKILL_STOCK + vouchers.getId(), 50, TimeUnit.MINUTES);
        System.out.println("id+++=+" + vouchers.getId());
        //初始化订单消息
        stringRedisTemplate.opsForSet().add("seckill_order:" + vouchers.getId().toString(), "INIT_PLACEHOLDER");
        stringRedisTemplate.expire("seckill_order:" + vouchers.getId().toString(), 50, TimeUnit.MINUTES);
        return voucher;
    }
}
